﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Messaging;
using Hydra.Shell;
using System.IO;
using Hydra.Shell.Interfaces;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace SolutionFramework
{
    public class PackageAgent : IPackageAgent
    {
        private Queue<PackageMessage> queuedMessages;
        private bool successSending;
        private bool waitingForPing;

        private class PackageMessage
        {
            public string MessageText { get; private set; }

            public PackageMessage(string message)
            {
                this.MessageText =  message;
            }
        }

        public PackageAgent()
        {
            queuedMessages = new Queue<PackageMessage>();
        }

        private void SendMessageNoQueue(PackageMessage message)
        {
            try
            {
                var sender = new LocalMessageSender("PackageMonitor", System.Windows.Messaging.LocalMessageSender.Global);

                sender.SendCompleted += (sender2, e) =>
                {
                    if (e.Error != null)
                    {
                        successSending = false;
                    }
                    else
                    {
                        Debug.Assert(message.MessageText == e.Message);

                        if (queuedMessages.Contains(message))
                        {
                            var messageRemoved = queuedMessages.Dequeue();

                            Debug.Assert(messageRemoved == message);
                        }

                        if (queuedMessages.Count > 0)
                        {
                            var queuedMessage = queuedMessages.Peek();

                            SendMessageNoQueue(queuedMessage);
                        }
                        else
                        {
                            successSending = true;
                            waitingForPing = false;
                        }
                    }
                };

                sender.SendAsync(message.MessageText);
            }
            catch
            {
            }
        }

        private void SendMessage(string message)
        {
            try
            {
                var sender = new LocalMessageSender("PackageMonitor", System.Windows.Messaging.LocalMessageSender.Global);

                sender.SendCompleted += (sender2, e) =>
                {
                    if (e.Error != null)
                    {
                        queuedMessages.Enqueue(new PackageMessage(message));
                        successSending = false;
                    }
                    else if (queuedMessages.Count > 0)
                    {
                        var queuedMessage = queuedMessages.Peek();

                        SendMessageNoQueue(queuedMessage);
                    }
                };

                if (!successSending)
                {
                    queuedMessages.Enqueue(new PackageMessage(message));

                    if (!waitingForPing)
                    {
                        SendMessageNoQueue(new PackageMessage("Ping"));
                        waitingForPing = true;
                    }
                }
                else
                {
                    if (queuedMessages.Count > 0)
                    {
                        Debugger.Break();
                    }

                    sender.SendAsync(message);
                }
            }
            catch
            {
                Debugger.Break();
            }
        }

        public void SendGeneralMessage(string message)
        {
            message = "GeneralMessage\r\n" + message;

            SendMessage(message);
        }

        public void SendGeneralError(Exception ex)
        {
            var message = "GeneralException\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace;

            SendMessage(message);
        }

        public void SendPackageMessage(IHydraPackageListItem package, PackageMessageType messageType, string extra)
        {
            var packageName = Path.GetFileNameWithoutExtension(package.FileName) + " {" + package.PackageGuid.ToString() + "}";
            var message = "PackageMessage\r\n" + packageName + "\r\n" + Enum.GetName(typeof(PackageMessageType), messageType) + "\r\n" + extra;

            SendMessage(message);
        }

        public void SendPackageError(IHydraPackageListItem package, Exception ex)
        {
            var packageName = Path.GetFileNameWithoutExtension(package.FileName) + " {" + package.PackageGuid.ToString() + "}";
            var message = "PackageException\r\n" + packageName + "\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace;

            SendMessage(message);
        }

        public void SendPackageMessage(IHydraPackage package, PackageMessageType messageType, string extra)
        {
            var packageName = package.GetType().Name + " {" + package.PackageGuid.ToString() + "}";
            var message = "PackageMessage\r\n" + packageName + "\r\n" + Enum.GetName(typeof(PackageMessageType), messageType) + "\r\n" + extra;

            SendMessage(message);
        }

        public void SendPackageError(IHydraPackage package, Exception ex)
        {
            var packageName = package.GetType().Name + " {" + package.PackageGuid.ToString() + "}";
            var message = "PackageException\r\n" + packageName + "\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace;

            SendMessage(message);
        }

        public void SendPackageMessage(Assembly packageAssembly, Guid packageGuid, PackageMessageType messageType, string extra)
        {
            var packageName = Path.GetFileNameWithoutExtension(packageAssembly.ManifestModule.ToString()) + " {" + packageGuid.ToString() + "}";
            var message = "PackageMessage\r\n" + packageName + "\r\n" + Enum.GetName(typeof(PackageMessageType), messageType) + "\r\n" + extra;

            SendMessage(message);
        }

        public void SendPackageError(Assembly packageAssembly, Guid packageGuid, Exception ex)
        {
            var packageName = Path.GetFileNameWithoutExtension(packageAssembly.ManifestModule.ToString()) + " {" + packageGuid.ToString() + "}";
            var message = "PackageException\r\n" + packageName + "\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace;

            SendMessage(message);
        }
    }
}
